<?php

/**
 * This file is part of the Codizer package.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * @copyright   Copyright (c) 2010 Advisa (http://www.advisa.fr)
 * @author      Codizer Core Team <team@codizer.org>
 * @package     Codizer
 */

define("DATE_FILE_PATH", FRAMEWORK_HOME.DS.'date'.DS.'locales'.DS.'date.xml');

class f_date_DateFormat
{

	/**
	 * Format a date with the given format
	 * 
	 * @example format("2008-09-11 12:02:04", "dmY", "fr");
	 *
	 * @param f_date_Date|string $date
	 * @param string $format
	 * @param string $lang
	 * @return string
	 */
	public static function format($date, $format, $lang = null)
	{
		if(is_string($date) && is_string($format))
		{
			$date = f_date_Date::getInstance($date);
		}

		if(is_null($lang))
		{
			$lang = f_core_Context::getInstance()->getDisplayLang();
		}

		$result = '';
		$escaped = false;
		for ($i = 0 ; $i < strlen($format) ; $i++)
		{
			$c = substr($format, $i, 1);
			if ($c == '\\')
			{
				if ($escaped)
				{
					$result .= '\\';
				}
				$escaped = ! $escaped;
			}
			else if ( $escaped )
			{
				$result .= $c;
				$escaped = false;
			}
			else
			{
				switch ($c)
				{
					// Day of the month, 2 digits with leading zeros
					case 'd' :
						$result .= sprintf('%02d', $date->getDay());
						break;

					// A textual representation of a day, three letters
					case 'D' :
						$result .= f_locale_Manager::getLocaleFromXML(DATE_FILE_PATH, "abbr.".strtolower(date("l", strtotime($date->toString()))), $lang);
						break;

					// Day of the month without leading zeros
					case 'j' :
						$result .= strval($date->getDay());
						break;

					// A full textual representation of the day of the week
					case 'l' :
                        $result .= f_locale_Manager::getLocaleFromXML(DATE_FILE_PATH, strtolower(date("l", strtotime($date->toString()))), $lang);
						break;

					// English ordinal suffix for the day of the month, 2 characters
					case 'S' :
						if ( array_key_exists(strval($date->getDay()), self::$englishOrdinalSuffixArray) )
						{
							$result .= self::$englishOrdinalSuffixArray[strval($date->getDay())];
						}
						else
						{
							$result .= self::$englishOrdinalSuffixArray['default'];
						}
						break;

					// Numeric representation of the day of the week
					case 'w' :
						$result .= strval($date->getDayOfWeek());
						break;

					// A full textual representation of a month, such as January or March
					case 'F' :
                        $result .= f_locale_Manager::getLocaleFromXML(DATE_FILE_PATH, strtolower(date("F", strtotime($date->toString()))), $lang);
						break;

					// Numeric representation of a month, with leading zeros
					case 'm' :
						$result .= sprintf('%02d', $date->getMonth());
						break;

					// A short textual representation of a month, three letters
					case 'M' :
                        $result .= f_locale_Manager::getLocaleFromXML(DATE_FILE_PATH, "abbr.".strtolower(date("F", strtotime($date->toString()))), $lang);
						break;

					// Numeric representation of a month, without leading zeros
					case 'n' :
						$result .= strval($date->getMonth());
						break;

					// Number of days in the given month
					case 't' :
						$result .= strval($date->getDaysInMonth());
						break;

					// The day of the year (starting from 0)
					case 'z' :
						$result .= strval($date->getDayOfYear());
						break;

					// Whether it's a leap year
					case 'L' :
						$result .= $date->isLeapYear() ? '1' : '0';
						break;

					// A full numeric representation of a year, 4 digits
					case 'Y' :
						$result .= sprintf('%04d', $date->getYear());
						break;

					// A two digit representation of a year
					case 'y' :
						$result .= substr(strval($date->getYear()), -2);
						break;

					// Lowercase Ante meridiem and Post meridiem
					case 'a' :
						$result .= ($date->getHour() < 12) ? 'am' : 'pm';
						break;

					// Uppercase Ante meridiem and Post meridiem
					case 'A' :
						$result .= ($date->getHour() < 12) ? 'AM' : 'PM';
						break;

					// 12-hour format of an hour without leading zeros
					case 'g' :
						$result .= strval($date->getHour() % 12);
						break;

					// 24-hour format of an hour without leading zeros
					case 'G' :
						$result .= strval($date->getHour());
						break;

					// 12-hour format of an hour with leading zeros
					case 'h' :
						$result .= sprintf('%02d', $date->getHour() % 12);
						break;

					// 24-hour format of an hour with leading zeros
					case 'H' :
						$result .= sprintf('%02d', $date->getHour());
						break;

					// Minutes with leading zeros
					case 'i' :
						$result .= sprintf('%02d', $date->getMinutes());
						break;

					// Seconds with leading zeros
					case 's' :
						$result .= sprintf('%02d', $date->getSeconds());
						break;

					default :
						$result .= $c;
				}
			}
		}
		return $result;
	}
	
	/**
	 * Format a date with the localized-data-time-format smart format
	 * 
	 * @example formatByLang("2008-09-11 12:02:04", "fr"); returns "11/09/2008 12:02"
	 *
	 * @param f_date_Date|string $date
	 * @param string|null $lang
	 * @return string
	 */
	public static function formatByLang($date, $lang = null)
	{
        return self::smartFormatByLang($date, "localized-data-time-format", $lang);
	}	
	
	/**
	 * Format a date with a smart format
	 *
	 * @example smartFormatByLang("2008-09-11 12:02:04", "localized-user-format", "fr"); returns "jeudi 11 septembre 2008"
	 * 
	 * @param f_date_Date|string $date
	 * @param string $format
	 * @param string|null $lang
	 * @return string
	 */
	public static function smartFormatByLang($date, $format, $lang = null)
	{
		if(is_null($lang))
		{
			$lang = f_core_Context::getInstance()->getDisplayLang();
		}		

        $format = f_locale_Manager::getLocaleFromXML(DATE_FILE_PATH, $format, $lang);

		return self::format($date, $format, $lang);
	}

    public static function httpFormat($date)
    {
        return gmdate('D, d M Y H:i:s', strtotime($date->toString())).' GMT';
    }

    /**
     * Return formatted date from today
     *
     * @example : 10:02, 24 may, 24/05/2009
     *
     * @param f_date_Date $date
     * @param string $lang
     * @return string
     */
    public static function elapsed($date, $lang = null)
    {
        if(is_string($date))
        {
            $date = f_date_Date::getInstance($date);
        }

        $now = f_date_Date::getInstance();

        if($date->getYear() == $now->getYear() && $date->getMonth() == $now->getMonth() && $date->getDay() == $now->getDay())
        {
            return date('H:i', $date->getTimeStamp());
        }

        if($date->getYear() == $now->getYear())
        {
            return self::smartFormatByLang($date, 'localized-day-month');
        }

        return self::smartFormatByLang($date, 'localized-data-format');
    }

    /**
     * Return time elapsed since given date in string
     *
     * @example : 2 hours ago, 1 week ago, etc...
     *
     * @param f_date_Date $date
     * @param string $lang
     * @return string
     */
    public static function timeSince($date, $lang = null)
    {
        if(is_object($date) && $date instanceof f_date_Date)
        {
            $seconds = time() - $date->getTimeStamp();
        }
        else
        {
            $seconds = time() - strtotime($date);
        }

        $minutes = 0;
        $hours = 0;
        $days = 0;
        $weeks = 0;
        $months = 0;
        $years = 0;

        if ($seconds == 0) $seconds = 1;
        if ($seconds > 60)  { $minutes = $seconds/60;  } else { return self::formatSince($seconds, 'second', $lang); }
        if ($minutes >= 60) { $hours = $minutes/60;    } else { return self::formatSince($minutes, 'minute', $lang); }
        if ($hours >= 24)   { $days = $hours/24;       } else { return self::formatSince($hours, 'hour', $lang); }
        if ($days >= 7)   { $weeks = $days/7;        } else { return self::formatSince($days, 'day', $lang); }
        if ($weeks >= 4)  { $months = $weeks/4;      } else { return self::formatSince($weeks, 'week', $lang); }
        if ($months >= 12)
        {
            $years = $months/12;
            return self::formatSince($years, 'year', $lang);
        } 
        else
        {
            return self::formatSince($months, 'month', $lang);
        }
    }

    /**
     * Returns formatted since value
     *
     * @param integer $value
     * @param string $type
     * @param string $lang
     * @return string
     */
    private static function formatSince($value, $type, $lang = null)
    {
        $value = round($value);

		if(is_null($lang))
		{
			$lang = f_core_Context::getInstance()->getDisplayLang();
		}

        if ($value == 1) 
        {
            return f_locale_Manager::getLocaleFromXML(DATE_FILE_PATH, 'since-'.$type.'-ago', $lang);
        } 
        else
        {
            return f_locale_Manager::getLocaleFromXML(DATE_FILE_PATH, 'since-'.$type.'s-ago', $lang, array('time' => $value));
        }
    }
	
	/**
	 * Array of English ordinal suffixes.
	 *
	 * @var array<string,string>
	 */
	private static $englishOrdinalSuffixArray = array (
		'1'  => 'st',
		'21' => 'st',
		'31' => 'st',
		'2'  => 'nd',
		'22' => 'nd',
		'3'  => 'rd',
		'13' => 'rd',
		'23' => 'rd',
		'default' => 'th'
		);	
	
}